package server

import (
	"container/list"
	"sync"
	"time"

	"github.com/ldlw/wfrp/src/models/consts"
	"github.com/ldlw/wfrp/src/utils/conn"
	"github.com/ldlw/wfrp/src/utils/log"
)

type ProxyServer struct {
	Name       string
	Passwd     string
	BindAddr   string
	ListenPort int64
	Status     int64

	listener     *conn.Listener  // 接受来自远程用户的新连接(accept new connection from remote users)
	ctlMsgChan   chan int64      // 每次接受新用户连接时，向通道输入“1”(every time accept a new user conn, put "1" to the channel)
	cliConnChan  chan *conn.Conn // 这个通道用于从控制协程获取客户端连接(get client conns from control goroutine)
	userConnList *list.List      // 存储用户连接列表(store user conns)
	mutex        sync.Mutex
}

func (p *ProxyServer) Init() {
	p.Status = consts.Idle
	p.cliConnChan = make(chan *conn.Conn)
	p.ctlMsgChan = make(chan int64)
	// 创建一个新的空列表(list)
	// list.New() 是用于创建一个新的链表实例。这个链表将会用来存储用户连接（user connections）
	// 可以被用作代理服务器记录并管理当前连接的数据结构
	p.userConnList = list.New()
}

func (p *ProxyServer) Lock() {
	p.mutex.Lock()
}

func (p *ProxyServer) Unlock() {
	p.mutex.Unlock()
}

func (p *ProxyServer) WaitUserConn() (closeFlag bool) {
	closeFlag = false

	_, ok := <-p.ctlMsgChan
	if !ok {
		closeFlag = true
	}
	return
}

func (p *ProxyServer) GetNewCliConn(c *conn.Conn) {
	p.cliConnChan <- c
}

func (p *ProxyServer) Close() {
	p.Lock()
	p.Status = consts.Idle
	p.listener.Close()
	close(p.ctlMsgChan)
	close(p.cliConnChan)
	p.userConnList = list.New()
	p.Unlock()
}

// start listening for user conns
func (p *ProxyServer) Start() (err error) {
	// 初始化代理服务器，包括设置一些属性和数据结构
	p.Init()
	// 调用 conn.Listen 函数创建一个监听器，并把它赋给 p.listener。如果创建失败，则返回错误
	p.listener, err = conn.Listen(p.BindAddr, p.ListenPort)
	if err != nil {
		return err
	}
	// 将代理服务器的状态设置为工作状态（consts.Working）
	p.Status = consts.Working

	// start a goroutine for listener to accept user connection
	// 启动一个 goroutine 来监听用户连接。循环中，调用 p.listener.GetConn() 阻塞地获取用户连接。如果监听器被关闭，则记录日志并返回
	go func() {
		for {
			// block
			// if listener is closed, err returned
			c, err := p.listener.GetConn()
			if err != nil {
				log.Info("ProxyName [%s], listener is closed", p.Name)
				return
			}
			log.Debug("ProxyName [%s], get one new user conn [%s]", p.Name, c.GetRemoteAddr())

			// insert into list
			p.Lock()
			if p.Status != consts.Working {
				log.Debug("ProxyName [%s] is not working, new user conn close", p.Name)
				c.Close()
				p.Unlock()
				return
			}
			// 将新的用户连接添加到代理服务器的用户连接列表 p.userConnList 中
			p.userConnList.PushBack(c)
			p.Unlock()

			// put msg to control conn
			// 向控制连接消息通道 p.ctlMsgChan 发送一个消息
			p.ctlMsgChan <- 1

			// set timeout
			// 为用户连接设置超时机制。使用 time.AfterFunc 定时器，在指定的时间后判断连接是否超时。如果超时，则记录警告日志
			time.AfterFunc(time.Duration(UserConnTimeout)*time.Second, func() {
				p.Lock()
				defer p.Unlock()
				element := p.userConnList.Front()
				if element == nil {
					return
				}

				userConn := element.Value.(*conn.Conn)
				if userConn == c {
					log.Warn("ProxyName [%s], user conn [%s] timeout", p.Name, c.GetRemoteAddr())
				}
			})
		}
	}()

	// start another goroutine for join two conns from client and user
	// 启动另一个 goroutine 来处理从客户端和用户之间建立连接的过程。循环中，从 p.cliConnChan 接收客户端连接。如果通道关闭，则返回
	go func() {
		for {
			cliConn, ok := <-p.cliConnChan
			if !ok {
				return
			}

			p.Lock()
			element := p.userConnList.Front()

			var userConn *conn.Conn
			if element != nil {
				userConn = element.Value.(*conn.Conn)
				// 获取用户连接列表中的第一个连接，并从列表中移除
				p.userConnList.Remove(element)
			} else {
				cliConn.Close()
				p.Unlock()
				continue
			}
			p.Unlock()

			// msg will transfer to another without modifying
			// l means local, r means remote
			log.Debug("Join two conns, (l[%s] r[%s]) (l[%s] r[%s])", cliConn.GetLocalAddr(), cliConn.GetRemoteAddr(),
				userConn.GetLocalAddr(), userConn.GetRemoteAddr())
			// 将客户端连接和用户连接合并，通过调用 conn.Join 实现连接的转发
			go conn.Join(cliConn, userConn)
		}
	}()

	return nil
}
